﻿using Enyim.Caching;
using Enyim.Caching.Memcached;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Pay.Core;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Pay.Service.Cache {
    public class MemcachedHelper : ICache {
        private MemcachedClient _client = MemcachedClientService.Instance.Client;

        private const int _expirySeconds = 60;
        private JsonSerializerSettings jsonConfig = new JsonSerializerSettings() {
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            NullValueHandling = NullValueHandling.Ignore
        };

        public bool Set(string key, object value, int seconds = 0) {
            if (key == null) throw new ArgumentNullException("key");
            if (value != null) {
                if (seconds == 0) seconds = _expirySeconds;
                var dt = DateTime.Now;
                var timeSpan = dt.AddSeconds(seconds) - dt;
                var data = JsonConvert.SerializeObject(value, jsonConfig);
                //try {
                return _client.Store(StoreMode.Set, key, data, timeSpan);
                //} catch { }
            }
            return false;
        }

        public T GetOrCreate<T>(string cacheKey, Func<T> create, int seconds = int.MaxValue) {
            var tags = cacheKey.Split(':');
            var tag = tags[0];
            var key = tags[1];
            //try {
            var cacheValue = _client.Get(tag);
            //} catch {
            //}
            if (cacheValue != null) {
                var joject = JsonConvert.DeserializeObject<JObject>(cacheValue.ToString(), jsonConfig);
                JToken jtoken = null;
                if (joject.TryGetValue(key, out jtoken)) {
                    return jtoken.ToObject<T>();
                } else {
                    var result = create();
                    if (result == null) return result;
                    joject.Add(key, JToken.FromObject(result));
                    Set(tag, joject, seconds);
                    return result;
                }
            } else {
                var result = create();
                if (result == null) return result;
                var joject = new JObject();
                joject.Add(key, JToken.FromObject(result));
                Set(tag, joject, seconds);
                return result;
            }
        }

        public T Get<T>(string key) {
            if (key == null) throw new ArgumentNullException("key");

            var value = default(T);
            //try {
            var cacheValue = _client.Get(key);
            if (cacheValue != null) {
                value = JsonConvert.DeserializeObject<T>(cacheValue.ToString(), jsonConfig);
                return value;
            }
            //} catch { }
            return value;
        }

        public bool Remove(string key) {
            //try {
            return _client.Remove(key);
            //} catch { return false; }
        }

        public void Remove(IEnumerable<string> tags) {
            var count = tags.Count();
            if (count > 0) {
                tags.ForEach((tag) => {
                    _client.Remove(tag);
                });
                //try {
                //} catch { }
            }
        }

        public bool RemoveAll() {
            _client.FlushAll();
            return true;
        }

        private bool _disposed = false;
        public void _Dispose(bool disposable) {
            if (!_disposed) {
                _client.Dispose();
                _disposed = true;
            }
        }

        public void Dispose() {
            _Dispose(false);
        }
    }

    internal class MemcachedClientService {
        private readonly MemcachedClient _client = new MemcachedClient("memcached");
        private static readonly MemcachedClientService _instance = new MemcachedClientService();

        private MemcachedClientService() {
        }

        public MemcachedClient Client {
            get {
                return this._client;
            }
        }

        public static MemcachedClientService Instance {
            get {
                return _instance;
            }
        }
    }

}
